from pml import PML
from pml import Objects
from pml import Parser
from pml.Errors import PMSyntaxError

class Token(Objects.object.typeobject):
    def __init__(self, type: str | None = None, value: str | None = None, bp: None = None, ep: None = None):
        super().__init__('Token')
        self.type = type
        self.value = value
        self.bp = bp
        self.ep = ep
    def __repr__(self):
        return f'Token({self.type}:{self.value})'

    def getObject(self):
        self.methods['__init__'] = self.tp_init

        return self

    @staticmethod
    def tp_init(frame, this, position):
        pass

class Lexer(Objects.object.typeobject):
    def __init__(self, position=None):
        super().__init__('Lexer')
        self.position = position
        self.char = ' '
        self.keywords = [i.obj.value for i in Parser.lexer.constants.TH_KEYWORDS]

    def getObject(self):
        self.methods['__init__'] = self.tp_init

        self.methods['lexer'] = self.lexer

        return self

    @staticmethod
    def lexer(frame, this: 'Lexer', position):
        tokens = []

        while this.char != Parser.lexer.position.Position.EOF.obj.value:
            if this.char in '\n \t':
                this.char = this.position.advanceReadNextCharacter(frame, this.position, position=None).obj.value
                continue

            if this.char in Parser.lexer.constants.TH_NUMBERS.obj.value:
                tokens.append(this.BuildTheTokenOfNumbers(this.position.copy(frame, this.position, position=None)))
            elif this.char in Parser.lexer.constants.TH_LETTERS.obj.value + '_':
                tokens.append(this.BuildTheTokenOfName(this.position.copy(frame, this.position, position=None)))
            elif this.char in '`\'"':
                tokens.append(this.BuildTheTokenOfString(this.position.copy(frame, this.position, position=None)))
            else:
                pos = this.position.copy(frame, this.position, position=None)
                if this.char in '+-*/%=.':
                    tokens.append(Objects.object.PMTypeObject(Token(Parser.lexer.constants.TT_OPERATION,
                        Parser.lexer.constants.TV_ADD if this.char == '+' else (
                            Parser.lexer.constants.TV_SUB if this.char == '-' else (
                                Parser.lexer.constants.TV_MUL if this.char == '*' else (
                                    Parser.lexer.constants.TV_DIV if this.char == '/' else (
                                        Parser.lexer.constants.TV_MOD if this.char == '%' else (
                                            Parser.lexer.constants.TV_EQ if this.char == '=' else Parser.lexer.constants.TV_POINT
                                        )
                                    )
                                )
                            )
                        ), pos, pos)))

                elif this.char == ',':
                    tokens.append(Objects.object.PMTypeObject(Token(Parser.lexer.constants.TT_SIGN, Parser.lexer.constants.TV_COMMA, pos, pos).getObject()))
                elif this.char == '(':
                    tokens.append(Objects.object.PMTypeObject(Token(Parser.lexer.constants.TT_SIGN, Parser.lexer.constants.TV_LP1, pos, pos).getObject()))
                elif this.char == ')':
                    tokens.append(Objects.object.PMTypeObject(Token(Parser.lexer.constants.TT_SIGN, Parser.lexer.constants.TV_RP1, pos, pos).getObject()))

                else:
                    pass

                this.char = this.position.advanceReadNextCharacter(frame, this.position, position=None).obj.value

        tokens.append(Objects.object.PMTypeObject(Token(Parser.lexer.constants.TT_EOF, Parser.lexer.constants.TV_EOF).getObject()))
        return PML.object.ChangeObject.PMLList_FromList(tokens)

    def BuildTheTokenOfNumbers(self, bp):
        s = ''
        point = 0.0
        while self.char != Parser.lexer.position.Position.EOF.obj.value and self.char in Parser.lexer.constants.TH_NUMBERS.obj.value + '_.':
            if self.char == '.':
                point += 1.0
                if point == 1.0:
                    s += '.'
            elif self.char != '_':
                s += self.char
            self.char = self.position.advanceReadNextCharacter(None, self.position, position=None).obj.value
        return Objects.object.PMTypeObject(Token(Parser.lexer.constants.TT_FLOAT if point else Parser.lexer.constants.TT_INT, s, bp, self.position.copy(None, self.position, position=None)).getObject())

    def BuildTheTokenOfName(self, bp):
        s = ''
        while self.char != Parser.lexer.position.Position.EOF.obj.value and self.char in Parser.lexer.constants.TH_LETTERS.obj.value + Parser.lexer.constants.TH_NUMBERS.obj.value + '_':
            s += self.char
            self.char = self.position.advanceReadNextCharacter(None, self.position, position=None).obj.value
        ep = bp.obj.copy(None, bp.obj, position=None)
        ep.obj.column.obj.value += len(s) - 1
        if s in self.keywords:
            if s == Parser.lexer.constants.TV_SET_VALUE:
                return Objects.object.PMTypeObject(Token(Parser.lexer.constants.TT_OPERATION, Parser.lexer.constants.TV_SET_VALUE, bp, self.position.copy(None, self.position, position=None)).getObject())
            return Objects.object.PMTypeObject(Token(Parser.lexer.constants.TT_KEYWORD, s, bp, ep).getObject())
        else:
            return Objects.object.PMTypeObject(Token(Parser.lexer.constants.TT_NAME, s, bp, ep).getObject())

    def BuildTheTokenOfString(self, bp):
        begin = self.char
        self.char = self.position.advanceReadNextCharacter(None, self.position, position=None).obj.value
        s = ''
        while self.char != Parser.lexer.position.Position.EOF.obj.value and self.char != begin:
            s += self.char
            self.char = self.position.advanceReadNextCharacter(None, self.position, position=None).obj.value
        self.char = self.position.advanceReadNextCharacter(None, self.position, position=None).obj.value
        return Objects.object.PMTypeObject(Token(Parser.lexer.constants.TT_CHAR if begin == "'" else Parser.lexer.constants.TT_STRING,
                                                 eval(s.__repr__().replace('\\\\', '\\')) if begin in '"\'' else s, bp, self.position.copy(None, self.position, position=None)).getObject())

    @staticmethod
    def tp_init(frame, this, position):
        pass
